#ifndef _ANIMPARAMETER_H_
#define _ANIMPARAMETER_H_
#include <SECore/Util.h>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <map>
#include <SECore/Ecs/Actor.h>

namespace SEngine
{

    class IAnimParamter 
    {
    public:
        IAnimParamter(Actor & actor) : m_actor(&actor) {}
        virtual ~IAnimParamter() = default;
        virtual void MoveTo(size_t ms) = 0;
        virtual void AddKeyFrame(size_t ms) = 0;
        virtual void RemoveKeyFrame(size_t ms) = 0;
        virtual std::string Name() = 0;
        void SetActor(Actor & actor) { m_actor = &actor; }
        
    protected:
        Actor * m_actor;
    };

    using AnimParamters = TypesLoader<
        int,
        float,
        glm::vec3,
        glm::quat
    >;

    class Mixer
    {
    public:
        template<typename T>
        T operator() (T min, T max, float a) {
            return glm::mix(min, max, a);
        }
    };

    template<typename Comp, typename FieldInfo, typename LerpFunc = Mixer>
    class AnimParamter : public IAnimParamter
    {
    public:
        AnimParamter(Actor & actor, FieldInfo info) : IAnimParamter(actor), m_fieldInfo(info) {}
        virtual void MoveTo(size_t ms) override {
            auto lastIter = m_keyFrame.begin();
            for (auto iter = m_keyFrame.begin(); iter != m_keyFrame.end(); iter++) {
                if (lastIter != iter && lastIter->first <= ms && ms < iter->first) {
                    float a = ms * 1.0f / (iter->first - lastIter->first);
                    auto val = LerpFunc()(lastIter->second, iter->second, a);
                    if (m_actor->HasComponent<Comp>()) {
                        auto & comp = m_actor->GetComponent<Comp>()->get();
                        m_fieldInfo.Value(comp, val);
                    }
                }
                lastIter = iter;
            }
        }
        virtual void AddKeyFrame(size_t ms) override {
            if (m_actor->HasComponent<Comp>()) {
                auto & comp = m_actor->GetComponent<Comp>()->get();
                auto val = m_fieldInfo.Value(comp);
                m_keyFrame[ms] = val;
            }
        }

        virtual void RemoveKeyFrame(size_t ms) override {
            m_keyFrame.erase(ms);
        }

        virtual std::string Name() override {
            return m_fieldInfo.name;
        }

    private:
        std::map<size_t, typename FieldInfo::Type> m_keyFrame;
        FieldInfo m_fieldInfo;
    };

    // class AnimParamter
    // {
    // public:
    //     using LerpFunc = std::function<AnimParamters::Variant(AnimParamters::Variant, AnimParamters::Variant, float)>;
    // public:
    //     template<typename T>
    //     AnimParamter(const std::string & name_,  T & data_) : m_name(name_), m_data(&data_) { 
    //         m_lerpFunction = [](AnimParamters::Variant v1, AnimParamters::Variant v2, float a) { 
    //             AnimParamters::Variant out;
    //             std::visit([&](auto val1, auto val2) {
    //                 if constexpr(std::is_same_v<std::decay_t<decltype(val1)>, std::decay_t<decltype(val2)>>)
    //                     out = glm::mix(val1, val2, a);
    //             }, v1, v2);
    //             return out;
    //         }; 
    //     }
    //     template<typename F>
    //     void Visit(F && fn) {
    //         std::visit([&fn](auto valPtr) {
    //             fn(*valPtr);
    //         }, m_data);
    //     }
    //     template<typename T>
    //     void Set(T val) { 
    //         Visit([&](auto & data) { if constexpr(std::is_same_v<std::decay_t<decltype(data)>, T>) data = val; });
    //     }
    //     template<typename T>
    //     T Get() {
    //         T val;
    //         Visit([&val](auto & data) { if constexpr(std::is_same_v<std::decay_t<decltype(data)>, T>) val = data; });
    //         return val;
    //     }
    //     std::string Name() { return m_name; }

    //     template<typename T>
    //     void AddKeyFrame(size_t ms, T val) {
    //         RemoveFrame(ms);
    //         m_keyFrames.emplace(ms, val);
    //     }

    //     void AddKeyFrame(size_t ms) {
    //         RemoveFrame(ms);
    //         Visit([&](auto & data) {
    //             m_keyFrames.emplace(ms, data);
    //         });
    //     }

    //     void RemoveFrame(size_t ms) {
    //         if (m_keyFrames.find(ms) != m_keyFrames.end()) {
    //             m_keyFrames.erase(ms);
    //         }
    //     }

    //     void MoveTo(size_t ms) {
    //         auto lastIter = m_keyFrames.begin();
    //         for (auto iter = m_keyFrames.begin(); iter != m_keyFrames.end(); iter++) {
    //             if (lastIter != iter && lastIter->first <= ms && ms <= iter->first) {
    //                 float a = ms * 1.0f / (iter->first - lastIter->first);
    //                 std::visit([](auto & ptr, auto val) {
    //                     if constexpr(std::is_same_v<std::decay_t<decltype(*ptr)>, std::decay_t<decltype(val)>>)
    //                         *ptr = val;
    //                 }, m_data, m_lerpFunction(lastIter->second, iter->second, a));
    //             }
    //             lastIter = iter;
    //         }
    //     }

    // private:
    //     std::string m_name;
    //     AnimParamters::VariantPtr m_data;
    //     std::map<size_t, AnimParamters::Variant> m_keyFrames;
    //     LerpFunc m_lerpFunction;
    // };
} // namespace SEngine


#endif